/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.engine.core.action;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.bef.component.detailcmpentity.vm.VMComponent;
import com.inspur.edp.bef.component.detailcmpentity.vm.VMMethod;
import com.inspur.edp.bff.engine.core.exception.BffEngineCoreException;
import com.inspur.edp.bff.engine.core.exception.BffEngineCoreErrorCodes;
import com.inspur.edp.bff.engine.core.common.VoActionUtil;
import com.inspur.edp.cdf.component.api.service.ComponentInvokeService;
import com.inspur.edp.common.component.workflow.dto.ApprovePayload;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.action.MappedCdpAction;
import com.inspur.edp.formserver.viewmodel.action.MappedCdpActionBase;
import com.inspur.edp.formserver.viewmodel.action.ViewModelAction;
import com.inspur.edp.formserver.viewmodel.action.viewmodelbase.ViewModelParameter;
import com.inspur.edp.formserver.viewmodel.collection.VMActionCollection;
import com.inspur.edp.formserver.viewmodel.common.VMCollectionParameterType;
import com.inspur.edp.metadata.rtcustomization.api.CustomizationRtService;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import lombok.var;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;

public class BffEngineAssemblerBase {

    private static CustomizationRtService mdService = SpringBeanUtils.getBean(CustomizationRtService.class);

    public static <T> ArrayList<T> getActions(Class<T> t, VMActionCollection actions, ArrayList<Class> paramTypes, ArrayList<Object> paramValues, T defaultAction, String defaultActionId) {
        java.util.ArrayList<T> list = new java.util.ArrayList<>();
        if (actions == null || actions.size() < 1) {
            if (defaultAction != null)
                list.add(defaultAction);
            return list;
        }
        //ComponentInvokeService componentInvokeService = SpringBeanUtils.getBean(ComponentInvokeService.class);
        for (var action : actions) {
            if (action.getID().equals(defaultActionId)) {
                list.add(defaultAction);
                continue;
            }
            ArrayList<Object> params = (ArrayList) paramValues.clone();
            ArrayList<Class> types = (ArrayList<Class>) paramTypes.clone();
            T actionInstance = (T) getActionInstance(action, params, types, new LinkedHashMap<>());
          //  T actionInstance = (T) componentInvokeService.getInstance(((MappedCdpActionBase)action).getComponentEntityId(), params);
            list.add(actionInstance);
        }

        return list;
    }

    public static Object getActionInstance(ViewModelAction action, ArrayList<Object> params, ArrayList<Class> types, LinkedHashMap<String, Object> outterParams) {
        String typeName = getActionTypeName(action);
        try {
           // Class type = Class.forName(typeName);

            getActionParams(action, types, params, outterParams);
//            Constructor[] constructors = Class.forName(typeName).getConstructors();
//            var constructor = constructors[0];
//            for (Constructor cons : constructors) {
//                if (constructor.getGenericParameterTypes().length > cons.getGenericParameterTypes().length) {
//                    constructor = cons;
//                }
//            }
            ComponentInvokeService componentInvokeService = SpringBeanUtils.getBean(ComponentInvokeService.class);
            return componentInvokeService.getInstance(((MappedCdpActionBase)action).getComponentEntityId(), params);
            //return  constructor.newInstance(params.toArray());
        } catch (Exception e) {
            throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1001,action.getName());
        }

    }

    public static Object getActionInstance(GspViewModel vo, ViewModelAction action, ArrayList<Object> params, ArrayList<Class> types, LinkedHashMap<String, Object> outterParams) {
        String typeName = getActionTypeName(action);
        try {
        //   Class type = Class.forName(typeName);

            getActionParams(action, types, params, outterParams);
//                var ctor = type.getConstructor(l);
            MappedCdpAction mappedCdpAction = (MappedCdpAction) action;
//            Constructor[] constructors = Class.forName(typeName).getConstructors();
//            var constructor = constructors[0];
            if (VoActionUtil.isBuildInAction(mappedCdpAction.getComponentEntityId())) {
                String targetNode = getNodeCode(params);
                String propName = VoActionUtil.getPropName(vo, mappedCdpAction.getComponentEntityId(), targetNode);
                params.add(propName);
                //获取参数最多的构造函数
//                for (Constructor cons : constructors) {
//                    if (constructor.getGenericParameterTypes().length < cons.getGenericParameterTypes().length) {
//                        constructor = cons;
//                    }
//                }
            }
            /*else {
                //获取参数最少的构造函数
                for (Constructor cons : constructors) {
                    if (constructor.getGenericParameterTypes().length > cons.getGenericParameterTypes().length) {
                        constructor = cons;
                    }
                }
            }*/
            ComponentInvokeService componentInvokeService = SpringBeanUtils.getBean(ComponentInvokeService.class);
            return componentInvokeService.getInstance(mappedCdpAction.getComponentEntityId(), params);
            //return constructor.newInstance(params.toArray());
        } catch (Exception e) {
            throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1001,action.getName());
        }

    }

    private static String getNodeCode(ArrayList<Object> params) {
        Object para = params.get(0);
        if (para instanceof ApprovePayload) {
            List<String> ns = ((ApprovePayload) para).getNodes();
            if (ns == null || ns.size() <= 0)
                return null;
            return ns.get(ns.size() - 1);
        }
        if (List.class.isAssignableFrom(para.getClass())) {
            List<Object> nodes = (ArrayList) para;
            if (nodes == null || nodes.size() <= 0)
                return null;
            String node = (String) nodes.get(nodes.size() - 1);
            return node;
        }
        return null;
    }

    private static String getActionTypeName(ViewModelAction action) {
        MappedCdpAction mappedCdpAction = (MappedCdpAction) action;
        VMComponent compMetadata = CMEngineUtil.getMetadataContent(mappedCdpAction.getComponentEntityId());
        if (compMetadata == null) {
            String[] messageParams = new String[]{ mappedCdpAction.getName() , mappedCdpAction.getComponentEntityId()};
            throw new BffEngineCoreException(BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1003,messageParams);
        }
        VMMethod method = compMetadata.getVmMethod();
        return method.getClassName();
    }

    private static void getActionParams(ViewModelAction action, ArrayList<Class> paramTypes, ArrayList<Object> values, LinkedHashMap<String, Object> outterParams) {

        if (action.getParameterCollection() == null || action.getParameterCollection().getCount() < 1)
            return;

        for (var param :
            action.getParameterCollection()) {
            ViewModelParameter parameter = (ViewModelParameter) param;
            Class paramType = getNativeType(parameter);
            paramTypes.add(paramType);

            if (outterParams.containsKey(parameter.getParamCode())) {
                values.add(outterParams.get(parameter.getParamCode()));
            } else {
                Object value = getParamValue(parameter, parameter.getActualValue().getValue());
                values.add(value);
            }
        }
    }

    private static Object getParamValue(ViewModelParameter parameter, String value) {
        Object realValue = null;
        switch (parameter.getParameterType()) {
            case Boolean:
                realValue = Boolean.parseBoolean(value);
                break;
            case DateTime:
                realValue = getDateValue(value);
                break;
            case Decimal:
                realValue = new BigDecimal(value);
                break;
            case Double:
                realValue = new Double(value);
                break;
            case Int32:
                realValue = Integer.parseInt(value);
                break;
            case Object:
            case String:
                if (parameter.getCollectionParameterType() == VMCollectionParameterType.Array) {
                    try {
                        realValue = new ObjectMapper().readValue(value, String[].class);

                    } catch (IOException e) {
                        throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1004,value);
                    }
                } else if (parameter.getCollectionParameterType() == VMCollectionParameterType.List) {
                    ObjectMapper mapper = new ObjectMapper();
                    JavaType javaType = mapper.getTypeFactory().constructParametricType(ArrayList.class, String.class);
                    try {
                        realValue = mapper.readValue(value, javaType);
                    } catch (IOException e) {
                        throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1005,value);
                    }
                } else {
                    realValue = value;
                }

                break;
            case Custom:
                if (parameter.getClassName() == null || "".equals(parameter.getClassName())) {
                    throw new BffEngineCoreException(BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1006,parameter.getParamName());
                }
                try {
                    Class baseType = Class.forName(parameter.getClassName());
                    realValue = new ObjectMapper().readValue(value, baseType);
                } catch (ClassNotFoundException e) {
                    throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1007,parameter.getParamName(),parameter.getClassName());
                } catch (IOException e) {
                    throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1008,parameter.getParamName(),parameter.getClassName(),e.getMessage());
                }
                break;
            default:
                throw new IllegalArgumentException(String.valueOf(parameter.getParameterType()));
        }

        return realValue;
    }


    private static Date getDateValue(String value) {
        if (value != null && !value.equals("")) {
            SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd");

            try {
                return sDateFormat.parse(value);
            } catch (ParseException var4) {
                throw new BffEngineCoreException(var4,BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1009,value,var4.getMessage());
            }
        } else {
            return null;
        }
    }

    public static Class getNativeType(ViewModelParameter parameter) {

        String baseTypeName = null;
        switch (parameter.getParameterType()) {
            case Boolean:
                baseTypeName = Boolean.class.getTypeName();
                break;
            case DateTime:
                baseTypeName = Date.class.getTypeName();
                break;
            case Decimal:
                baseTypeName = BigDecimal.class.getTypeName();
                break;
            case Double:
                baseTypeName = Double.class.getTypeName();
                break;
            case Int32:
                baseTypeName = Integer.class.getTypeName();
                break;
            case Object:
                baseTypeName = Object.class.getTypeName();
                break;
            case String:
                baseTypeName = String.class.getTypeName();
                break;
            case Custom:
                if (parameter.getClassName() == null || "".equals(parameter.getClassName())) {
                    throw new BffEngineCoreException(BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1006,parameter.getParamName());
                }
                baseTypeName = parameter.getClassName();
                break;
            default:
                throw new IllegalArgumentException(String.valueOf(parameter.getParameterType()));
        }


        if (parameter.getCollectionParameterType() == VMCollectionParameterType.Array) {
            baseTypeName = "[L" + baseTypeName + ";";
        }
        Class baseType = null;
        try {
            baseType = Class.forName(baseTypeName);
        } catch (ClassNotFoundException e) {
            throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1007,parameter.getParamName(),parameter.getClassName());
        }

        if (parameter.getCollectionParameterType() == VMCollectionParameterType.List) {
            Class type = ArrayList.class;
//            var superType =
//                    type instanceof java.lang.reflect.Type
//                            ? ((java.lang.reflect.Type) type)
//                            : null;
//            var paramType = superType instanceof java.lang.reflect.ParameterizedType
//                    ? ((java.lang.reflect.ParameterizedType) superType)
//                    : null;
            return type;
        }


        return baseType;
    }

    public static Type getNativeTrueType(ViewModelParameter parameter) {
        String baseTypeName = null;
        switch (parameter.getParameterType()) {
            case Boolean:
                baseTypeName = Boolean.class.getTypeName();
                break;
            case DateTime:
                baseTypeName = Date.class.getTypeName();
                break;
            case Decimal:
                baseTypeName = BigDecimal.class.getTypeName();
                break;
            case Double:
                baseTypeName = Double.class.getTypeName();
                break;
            case Int32:
                baseTypeName = Integer.class.getTypeName();
                break;
            case Object:
                baseTypeName = Object.class.getTypeName();
                break;
            case String:
                baseTypeName = String.class.getTypeName();
                break;
            case Custom:
                if (parameter.getClassName() == null || "".equals(parameter.getClassName())) {
                    throw new BffEngineCoreException(BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1006,parameter.getParamName());
                }
                baseTypeName = parameter.getClassName();
                break;
            default:
                throw new IllegalArgumentException(String.valueOf(parameter.getParameterType()));
        }


        if (parameter.getCollectionParameterType() == VMCollectionParameterType.Array) {
            baseTypeName = "[L" + baseTypeName + ";";
        }
        Class baseType = null;
        try {
            baseType = Class.forName(baseTypeName);
        } catch (ClassNotFoundException e) {
            throw new BffEngineCoreException(e, BffEngineCoreErrorCodes.GSP_BFF_ENGINE_1007, parameter.getParamName(),parameter.getClassName());
        }

        if (parameter.getCollectionParameterType() == VMCollectionParameterType.List) {
            Class type = ArrayList.class;
            Type[] actualTypeArguments = new Type[1];
            actualTypeArguments[0] = baseType;
            ParameterizedTypeImpl.make(ArrayList.class, actualTypeArguments, ArrayList.class);
            return type;
        }


        return baseType;
    }
}
